﻿// © 2010 IDesign Inc. All rights reserved
//Questions? Comments? go to
//http://www.idesign.net

using System;
using System.Configuration;
using System.Diagnostics;
using System.Messaging;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.Text;

namespace ServiceModelEx
{
    public static class QueuedServiceHelper
    {
        private static readonly Lazy<MessageQueueAccessControlEntry> _ace = new Lazy<MessageQueueAccessControlEntry>(() =>
        {
            var sidEveryone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
            var accountNameEveryone = (NTAccount)sidEveryone.Translate(typeof(NTAccount));
            var trustee = new Trustee(accountNameEveryone.Value);
            var ace = new MessageQueueAccessControlEntry(trustee, MessageQueueAccessRights.FullControl);
            return ace;
        }, true);

        private static MessageQueueAccessControlEntry Ace { get { return _ace.Value; } }

        public static void EnsureQueuesAvailable()
        {
            ChannelEndpointElementCollection endpointElements = new ChannelEndpointElementCollection();
            var section = ConfigurationManager.GetSection("system.serviceModel/client") as System.ServiceModel.Configuration.ClientSection;
            if (section != null)
            {
                endpointElements = section.Endpoints;
            }

            foreach (ChannelEndpointElement endpointElement in endpointElements)
            {
                if (endpointElement.Binding == "netMsmqBinding")
                {
                    string queue = GetQueueNameFromUri(endpointElement.Address);

                    if (MessageQueue.Exists(queue) == false)
                    {
                        MessageQueue.Create(queue, true);
                    }
                }
            }
        }

        public static void EnsureQueueAvailable<T>(string endpointName) where T : class
        {
            ChannelFactory<T> factory = new ChannelFactory<T>(endpointName);
            factory.Endpoint.EnsureQueueAvailable();
        }

        public static void EnsureQueueAvailable<T>() where T : class
        {
            EnsureQueueAvailable<T>("");
        }

        public static void EnsureQueueAvailable(this ServiceEndpoint endpoint)
        {
            if (!(endpoint.Binding is NetMsmqBinding))
            {
                return;
            }

            string queueName = GetQueueNameFromUri(endpoint.Address.Uri);
            EnsureQueueAvailable(queueName);

            var binding = endpoint.Binding as NetMsmqBinding;
            if (binding.DeadLetterQueue == DeadLetterQueue.Custom)
            {
                Debug.Assert(binding.CustomDeadLetterQueue != null);
                string dlqName = GetQueueNameFromUri(binding.CustomDeadLetterQueue);
                EnsureQueueAvailable(dlqName);
            }
        }

        public static void EnsureQueueAvailable(string queueName)
        {
            if (MessageQueue.Exists(queueName) == false)
            {
                var mq = MessageQueue.Create(queueName, true);
                mq.SetPermissions(Ace);
            }
        }

        public static void PurgeQueue(ServiceEndpoint endpoint)
        {
            if (endpoint.Binding is NetMsmqBinding)
            {
                string queueName = GetQueueNameFromUri(endpoint.Address.Uri);

                if (MessageQueue.Exists(queueName) == true)
                {
                    MessageQueue queue = new MessageQueue(queueName);
                    queue.Purge();
                }
            }
        }

        internal static string GetQueueNameFromUri(Uri uri)
        {
            //Debug.Assert(uri.Segments.Length == 3 || uri.Segments.Length == 2);
            if (uri.Segments[1] == @"private/")
            {
                var sb = new StringBuilder(@".\private$\");
                for (int segmentIndex = 2; segmentIndex < uri.Segments.Length; ++segmentIndex)
                {
                    sb.Append(uri.Segments[segmentIndex]);
                }
                return sb.ToString();
            }

            var queueName = uri.Host;
            foreach (string segment in uri.Segments)
            {
                if (segment == "/")
                {
                    continue;
                }
                string localSegment = segment;
                if (segment[segment.Length - 1] == '/')
                {
                    localSegment = segment.Remove(segment.Length - 1);
                }
                queueName += @"\";
                queueName += localSegment;
            }
            return queueName;
        }
    }
}